package math.vectorSpace.R3.visualization;

import math.mathExtended.MathExtended;
import math.vectorSpace.R2.Point2D;
import math.vectorSpace.R3.Point3D;
import math.vectorSpace.R3.Vector3D;

public abstract class Render3D {

    private final int screenWidth, screenHeight;

    /** The plane is the viewer's screen */
    protected final Point3D planeOrigin, planeCenter;

    protected final Vector3D xBasis, yBasis;

    protected Render3D(double angleXY, double angleZ,
            int distance, int screenWidth, int screenHeight) {
        this.xBasis = new Vector3D(MathExtended.cosd(angleXY),
                MathExtended.sind(angleXY), 0);
        this.yBasis = new Vector3D(MathExtended.cosd(angleXY + 90)
                * MathExtended.sind(angleZ),
                MathExtended.sind(angleXY + 90) * MathExtended.sind(angleZ),
                MathExtended.cosd(angleZ));

        this.planeCenter = new Vector3D(distance, 0, 0).rotateZ(angleXY).
                rotateTowards(Vector3D.ZAXIS, angleZ).toPoint();


        Point3D temp = planeCenter.subtract(xBasis.scale(screenWidth / 2));
        this.planeOrigin = temp.subtract(yBasis.scale(screenHeight / 2));

        this.screenWidth = screenWidth;
        this.screenHeight = screenHeight;
    }

    public final int getScreenHeight() {
        return screenHeight;
    }

    /**
     * Returns true if the x-basis and y-basis are orthogonal, false otherwise.
     * The Applet will still run as normal, albeit the shapes will most likely
     * be distorted.
     * 
     * @return 
     */
    public final boolean checkBasis() {
        if (Math.abs(xBasis.dot(yBasis)) < 0.00000001) {
            return true;
        }
        return false;
    }

    /**
     * Returns the distance from the given Point3D to the Plane's origin
     * 
     * @param point
     * @return 
     */
    public final double distance(Point3D point) {
        return point.distance(planeOrigin);
    }

    /**
     * Returns a new Point2D that is the projection of the given Point3D, 
     * point, onto this plane's coordinate system.
     * 
     * @param point
     * @return a new Point2D that is the projection of the given Point3D onto
     * this plane.
     */
    public abstract Point2D project(Point3D point);
}
